package com.beans.factory;

public abstract class AbstractBeanFactory implements BeanFactory{

    /**
     * 返回一个实例
     * @param name 所需要检索的 bean 的名称
     * @param requiredType 所需要检索的 bean 的类型
     * @return 一个 bean 实例
     */
    protected <T> T doGetBean(String name, Class<T> requiredType){
        return null;
    }


    //---------------------------------------------------------------------
    // Implementation of BeanFactory interface
    //---------------------------------------------------------------------

    @Override
    public Object getBean(String name) throws Exception {
        return doGetBean(name, null, null, false);
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws Exception {
        return doGetBean(name, requiredType, null, false);
    }




    /**
     * Return an instance, which may be shared or independent, of the specified bean.
     * 返回一个实例，该实例可以是指定bean共享或是独有的。
     * @param name 要检索的bean的名称
     * @param requiredType 要检索的bean类型
     * @param args 使用显式参数创建Bean实例时要使用的参数
     * (仅在创建新实例而不是检索现有实例时适用)
     * @param typeCheckOnly 是否仅仅获取实例类型
     * @return 一个实例bean
     * @throws Exception 如果这个bean不能被创建
     */
    @SuppressWarnings("unchecked")
    protected <T> T doGetBean(
            String name, Class<T> requiredType, Object[] args, boolean typeCheckOnly)
            throws Exception {

        String beanName = name;

        // 返回值
        Object bean;

        Object sharedInstance = getSingleton(beanName);

        // 这里说下 args 参数，虽然看上去一点不重要。前面我们一路进来的时候都是 getBean(beanName)，
        // 所以 args 传参其实是 null 的，但是如果 args 不为空的时候，那么意味着调用方不是希望获取 Bean，而是创建 Bean
        if (sharedInstance != null && args == null) {
            // 下面这个方法：如果是普通 Bean 的话，直接返回 sharedInstance，
            // 如果是 FactoryBean 的话，返回它创建的那个实例对象
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }

        else {
            if (!typeCheckOnly) {
                // 如果当前 bean 不在正在创建中的 bean 的缓存集合（alreadyCreated）中，就加进Set中
                markBeanAsCreated(beanName);
            }
            try {
                // 合并要获取的 bean 的属性并把属性赋值给一个 RootBeanDefinition
                RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

                // 检查合并之后的 RootBeanDefinition 的是不是抽象的，还会检查如果
                // 获取 bean 的参数不空的情况下，bean 的类型是 singleton 的就会报错，
                // 因为单例情况下 bean 都是一样的，只有 prototype 情况下才能不一致
//                checkMergedBeanDefinition(mbd, beanName, args);

                // 如果是 singleton scope 的，创建 singleton 的实例
                if (mbd.isSingleton()) {
                    sharedInstance = getSingleton(beanName, () -> {
                        try {
                            // 重要，重要，重要
                            return createBean(beanName, mbd, args);
                        }
                        catch (Exception ex) {
                            destroySingleton(beanName);
                            throw ex;
                        }
                    });
                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                }

                // 如果是 prototype scope 的，创建 prototype 的实例
                else if (mbd.isPrototype()) {
                    // It's a prototype -> create a new instance.
                    Object prototypeInstance = null;
                    try {
                        // 将需要创建的bean的beanName放到ThreadLocal中
                        beforePrototypeCreation(beanName);
                        prototypeInstance = createBean(beanName, mbd, args);
                    }
                    finally {
                        // 将需要创建的bean的beanName从ThreadLocal中移除
                        afterPrototypeCreation(beanName);
                    }
                    bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                }
            }
            catch (Exception ex) {
                throw ex;
            }
        }
        return (T) bean;
    }

}
